﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using CodeDeeds.Reflection.Cloning;

namespace CloneFactoryTest
{
   public class Person
   {
      public Person()
      {
         Children = new List<Person>();
      }

      public string FirstName { get; set; }
      public string LastName { get; set; }
      public string NickName { get; set; }
      public DateTime? DateOfBirth { get; set; }
      public DateTime? DateOfDeath { get; set; }
      public Person Mother { get; set; }
      public Person Father { get; set; }
      public Person Spouse { get; set; }

      public List<Person> Children { get; private set; }


      public override string ToString()
      {
         return "Person: " + NickName;
      }

      public override bool Equals(object obj)
      {
         return EqualPersons(this, (Person)obj, 2);
      }

      protected static bool EqualPersons(Person p1, Person p2, int depth)
      {
         if(depth < 0)
         {
            return true;
         }

         if(null == p1 && null == p2) return true;
         if(null == p1 || null == p2) return false;

         --depth;

         bool equal = p1.FirstName == p2.FirstName &&
                      p1.LastName == p2.LastName &&
                      p1.NickName == p2.NickName &&
                      ((!p1.DateOfBirth.HasValue && !p2.DateOfBirth.HasValue) || (p1.DateOfBirth.HasValue && p2.DateOfBirth.HasValue && p1.DateOfBirth.Value == p2.DateOfBirth.Value)) &&
                      ((!p1.DateOfDeath.HasValue && !p2.DateOfDeath.HasValue) || (p1.DateOfDeath.HasValue && p2.DateOfDeath.HasValue && p1.DateOfDeath.Value == p2.DateOfDeath.Value)) &&
                      EqualPersons(p1.Mother, p2.Mother, depth) &&
                      EqualPersons(p1.Father,p2.Father, depth) &&
                      EqualPersons(p1.Spouse, p2.Spouse, depth) &&
                      ((p1.Children == null && p2.Children == null) || (p1.Children != null && p2.Children != null && p1.Children.Count == p2.Children.Count));

         if (equal)
         {
            for (int i = 0; i < p1.Children.Count; ++i)
            {
               if(!EqualPersons(p1.Children[i], p2.Children[i], depth))
               {
                  equal = false;
                  break;
               }
            }
         }

         return equal;
      }

      public bool IsHashCodeCalculated
      {
         get { return m_hashcode.HasValue; }
      }

      [CloneFieldBehaviour(CloneFieldBehaviour.ClearReference)]
      protected int? m_hashcode;
      
      public override int GetHashCode()
      {
         if(!m_hashcode.HasValue)
         {
            m_hashcode = 0;
            m_hashcode = CalculateHashCode();
         }
         return m_hashcode.Value;
      }

      protected virtual int CalculateHashCode()
      {
         int hash = (FirstName ?? string.Empty).GetHashCode();
         hash = hash << 1;
         hash ^= (LastName ?? string.Empty).GetHashCode();
         hash = hash << 1;
         hash ^= (NickName ?? string.Empty).GetHashCode();
         hash = hash << 1;
         hash ^= (DateOfBirth ?? DateTime.MinValue).GetHashCode();
         hash = hash << 1;
         hash ^= (DateOfDeath ?? DateTime.MinValue).GetHashCode();
         hash = hash << 1;
         hash ^= (null == Mother) ? 27 : Mother.GetHashCode();
         hash = hash << 1;
         hash ^= (null == Father) ? 27 : Father.GetHashCode();
         hash = hash << 1;
         hash ^= (null == Spouse) ? 27 : Spouse.GetHashCode();
         foreach(Person child in Children)
         {
            hash = hash << 1;
            hash ^= (null == child) ? 27 : child.GetHashCode();
         }

         return hash;
      }

   }
}
